如何避免将SQL_二进制数组数据作为参数传递时以零结尾?(ODBC驱动程序)

如何避免将SQL_二进制数组数据作为参数传递时以零结尾?(ODBC驱动程序),c,sql-server,windows,odbc,C,Sql Server,Windows,Odbc,我刚刚了解了Windows ODBC驱动程序API需要一个SQL\u二进制数据数组作为输入参数,以零字节终止。尽管我在文档中没有找到这样的语句,但我通过使用以下代码执行存储过程发现了这一点: 最小示例 //参数绑定 字节数据[10]={15,3,54,144,34,211,200,147,15,74}; SQLBindParameter(hstmt,1,SQL参数输入,SQL参数二进制,SQL参数二进制,10,0,数据,0,NULL); //程序执行 SQLRETURN res=SQLExecD

我刚刚了解了Windows ODBC驱动程序API需要一个
SQL\u二进制
数据数组作为输入参数,以零字节终止。尽管我在文档中没有找到这样的语句,但我通过使用以下代码执行存储过程发现了这一点:

最小示例

//参数绑定
字节数据[10]={15,3,54,144,34,211,200,147,15,74};
SQLBindParameter(hstmt,1,SQL参数输入,SQL参数二进制,SQL参数二进制,10,0,数据,0,NULL);
//程序执行
SQLRETURN res=SQLExecDirect(hstmt,(SQLCHAR*)“{call dbo.Update_Data(?)}”,SQL\NTS);
它导致
SQLExecDirect
SQL\u NULL\u数据
而失败。使用
SQLGetDiagRec
查询诊断记录时,我已收到该记录:

SQL State=22001,错误消息:“[Microsoft][ODBC SQL Server驱动程序] 字符串或二进制数据将被截断

虽然这通常表示插入或更新到列中的数据比列本身大,但这里的情况并非如此。在尝试了4个小时的不同参数和语句后,我终于发现解决方案非常简单,只需在最后一个位置用零终止字节数组:

//参数绑定

字节数据[11]={15,3,54,144,34,211,200,147,15,74,0};// 二进制数据必须以null结尾不是真的(如果是真的,则不能插入任何包含0值的数据,如
{100,0,100,0,100}

  • 您需要为缓冲区长度(缓冲区大小)设置正确的值
  • 您需要正确设置并初始化StrLen\u或\u IndPtr参数。对于二进制缓冲区,StrLen_或_IndPtr的值必须是缓冲区中保存的数据的长度。请注意,这不能与实际缓冲区大小相同(但必须是二进制数据必须以null结尾不是真的(如果是真的,则不能插入任何包含0值的数据,如
    {100,0,100,0,100}

  • 您需要为缓冲区长度(缓冲区大小)设置正确的值

  • 您需要正确设置并初始化StrLen_或_IndPtr参数。对于二进制缓冲区,StrLen_或_IndPtr的值必须是缓冲区中保存的数据的长度。请注意,这不得与实际缓冲区大小相同(但必须为“我刚刚了解到,Windows ODBC驱动程序API需要一个SQL_二进制数据数组作为输入参数,以零字节结尾。“-根据我的经验,这肯定不是真的。如果您认为是这样,请发布一些可编译的代码来演示它,并/或将其作为错误报告给ODBC驱动程序的供应商。@NeilButterworth您可能很幸运,在您绑定为参数的二进制数据之后,您的内存为零(在它工作时发生在我身上)。包含整个ODBC句柄分配和驱动程序连接将使此问题至少扩展50行代码,并且在我看来不再显示问题的最小示例。我通过直接从management studio执行来确保查询运行时没有错误。这肯定是ODBC驱动程序API的问题。您如果您设置了最新的参数(
    StrLen\u或\u IndPtr
    ),则需要指定函数,即
    SQLBindParameter(…,data,10,NULL);
    )不是
    NULL
    你得到了什么?它应该返回二进制数据的长度,对吗?@Vinzenz:如下所述,你必须同时设置。Buffer length和StrLen_或_IndPtr。StrLen_或_IndPtr不能总是与Buffer length相同的值。例如,考虑VARBINARY列。。“我刚刚了解到,Windows ODBC驱动程序API需要一个SQL_二进制数据数组作为输入参数,以零字节结尾。“-根据我的经验,这肯定不是真的。如果您认为是这样,请发布一些可编译的代码来演示它,并/或将其作为错误报告给ODBC驱动程序的供应商。@NeilButterworth您可能很幸运,在您绑定为参数的二进制数据之后,您的内存为零(在它工作时发生在我身上)。包含整个ODBC句柄分配和驱动程序连接将使此问题至少扩展50行代码,并且在我看来不再显示问题的最小示例。我通过直接从management studio执行来确保查询运行时没有错误。这肯定是ODBC驱动程序API的问题。您如果您设置了最新的参数(
    StrLen\u或\u IndPtr
    ),则需要指定函数,即
    SQLBindParameter(…,data,10,NULL);
    )你得到的不是
    NULL
    而是什么?它应该返回二进制数据的长度,对吗?@Vinzenz:如下所述,你必须同时设置。Buffer length和StrLen_或_IndPtr。StrLen_或_IndPtr不能总是与Buffer length相同的值。例如,考虑一个VARBINARY列..你对
    StrL绝对正确en_或_IndPtr
    参数!我刚刚调整了单元测试,将
    BufferLength
    StrLen或_IndPtr
    参数都设置为10,它起了作用!现在好奇的是,我测试了同样的事情,将
    BufferLength
    保留为
    0
    。甚至这也起到了作用。因此看起来
    BufferLength
    实际上正在被激活对于
    SQL\u C\u BINARY
    或与
    StrLen\u或\u IndPtr
    缓冲区相关的or。如果将
    BufferLength
    参数保留在
    0
    ,是否会得到相同的结果?如果是,您可以调整您的答案吗?除此之外,回答很好,感谢您花费时间!!它实际上可以用作if-else系统。如果
    如果给定StrLen_或_IndPtr
    参数,它将被解释为len
    #include <windows.h>
    #include <tchar.h>
    #include <iostream>
    #include <sql.h>
    #include <sqlext.h>
    #include <sqlucode.h>
    
    void printErr(SQLHANDLE handle, SQLSMALLINT handleType)
    {
        SQLSMALLINT recNr = 1;
        SQLRETURN ret = SQL_SUCCESS;
        while (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
        {
            SQLWCHAR errMsg[SQL_MAX_MESSAGE_LENGTH + 1];
            SQLWCHAR sqlState[5 + 1];
            errMsg[0] = 0;
            SQLINTEGER nativeError;
            SQLSMALLINT cb = 0;
            ret = SQLGetDiagRec(handleType, handle, recNr, sqlState, &nativeError, errMsg, SQL_MAX_MESSAGE_LENGTH + 1, &cb);
            if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
            {
                std::wcerr << L"ERROR; native: " << nativeError << L"; state: " << sqlState << L"; msg: " << errMsg << std::endl;
            }
            ++recNr;
        }
    }
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        // connect to db
        SQLRETURN   nResult = 0;
        SQLHANDLE   handleEnv = 0;
    
        nResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, (SQLHANDLE*)&handleEnv);
        nResult = SQLSetEnvAttr(handleEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3_80, SQL_IS_INTEGER);
    
        SQLHANDLE   handleDBC = 0;
        nResult = SQLAllocHandle(SQL_HANDLE_DBC, handleEnv, (SQLHANDLE*)&handleDBC);
    
        SQLWCHAR     strConnect[256] = L"Driver={SQL Server Native Client 11.0};Server=.\\TEST;Database=Foobar;Uid=Secret;Pwd=Secret";
        SQLWCHAR     strConnectOut[1024] = { 0 };
        SQLSMALLINT nNumOut = 0;
        nResult = SQLDriverConnect(handleDBC, NULL, (SQLWCHAR*)strConnect, SQL_NTS, (SQLWCHAR*)strConnectOut, sizeof(strConnectOut), &nNumOut, SQL_DRIVER_NOPROMPT);
        if (!SQL_SUCCEEDED(nResult))
            printErr(handleDBC, SQL_HANDLE_DBC);
    
        // Allocate a statement
        SQLHSTMT    handleStatement = SQL_NULL_HSTMT;
        nResult = SQLAllocHandle(SQL_HANDLE_STMT, handleDBC, (SQLHANDLE*)&handleStatement);
        if (!SQL_SUCCEEDED(nResult))
            printErr(handleDBC, SQL_HANDLE_DBC);
    
        int col = 0;
        SQLRETURN res = SQL_NTS;
    
        // Params
        SQLBIGINT id = 2;
        SQLCHAR data[10] = { 15, 3, 54, 144, 34, 211, 200, 147, 15, 74 };
        SQLLEN cbId = 0;
        SQLLEN cbData = 10;
    
        res = SQLBindParameter(handleStatement, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 0, 0, &id, sizeof(id), &cbId);
        if (!SQL_SUCCEEDED(res))
            printErr(handleStatement, SQL_HANDLE_STMT);
    
        res = SQLBindParameter(handleStatement, 2, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, 10, 0, data, sizeof(data), &cbData);
        if (!SQL_SUCCEEDED(res))
            printErr(handleStatement, SQL_HANDLE_STMT);
    
        // Execution
        res = SQLExecDirect(handleStatement, (SQLWCHAR*)L"{call dbo.Update_Store_Data(?,?)}", SQL_NTS);
        if (!SQL_SUCCEEDED(res))
        {
            printErr(handleStatement, SQL_HANDLE_STMT);
        }
    
        return 0;
    }