Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/130.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何修复C+中的手柄泄漏+;ODBC连接/断开过程?_C++_Sql Server_Windows 7_Odbc_Windows 10 - Fatal编程技术网

C++ 如何修复C+中的手柄泄漏+;ODBC连接/断开过程?

C++ 如何修复C+中的手柄泄漏+;ODBC连接/断开过程?,c++,sql-server,windows-7,odbc,windows-10,C++,Sql Server,Windows 7,Odbc,Windows 10,症状 在对我的C++应用程序进行负载测试时,它使用和ODBC连接不断地(并且大量)与SQL Server实例接口,我开始注意到Windows任务管理器中的句柄泄漏。这些以前没有(请抑制您的怀疑并继续阅读),我怀疑它们是在进行上述负载测试时开发的 在备用计算机上运行完全相同的二进制文件不会显示相同的症状,而是与泄漏开始之前的标准和常见的预期行为相关。也就是说,在不同的机器上使用相同的二进制文件不会发生泄漏 我将此句柄泄漏跟踪到SQL connect/disconnect进程,并能够使用控制台应用程

症状

在对我的C++应用程序进行负载测试时,它使用和ODBC连接不断地(并且大量)与SQL Server实例接口,我开始注意到Windows任务管理器中的句柄泄漏。这些以前没有(请抑制您的怀疑并继续阅读),我怀疑它们是在进行上述负载测试时开发的

在备用计算机上运行完全相同的二进制文件不会显示相同的症状,而是与泄漏开始之前的标准和常见的预期行为相关。也就是说,在不同的机器上使用相同的二进制文件不会发生泄漏

我将此句柄泄漏跟踪到SQL connect/disconnect进程,并能够使用控制台应用程序重新创建,该控制台应用程序仅打开和关闭与SQL Server实例的连接,该连接是以SQL Server实例为模型的。代码如下所示

  • 并非所有分配的句柄都已释放
  • 返回值显示在代码中执行的所有SQL操作没有错误
所用部件的技术细节

  • 开发机器(泄漏)。windows7prosp1。
    • 已尝试使用ODBC驱动程序:
    • SQL Server 6.01.7601.17514
    • SQL Server 2015.130.1601.05的ODBC驱动程序13
    • SQL Server 2015.131.4413.46的ODBC驱动程序13
  • 使用更新的操作系统开发计算机(泄漏)。Windows10Pro
    • 已尝试使用ODBC驱动程序:
    • SQL Server 10.00.15063.00
    • SQL Server 2017.140.500.272的ODBC驱动程序13
  • 备用机器(无泄漏)。Windows Server 2012 R2。
    • ODBC驱动程序:Sql Server 6.03.9600.17415
  • Visual Studio 2013(v120)控制台应用程序。
    • 标准windows库
    • 字符集未设置
    • 不支持CLR
    • 无完整程序或C++优化。
  • 已尝试从开发人员计算机进行SQL Server连接。
    • 快车9.0.5
    • 13.0.1728.2
什么尝试都没有成功

  • 正在连接到另一个SQL server实例

  • 使用不同的ODBC驱动程序

  • 修复Visual Studio 2013 Pro并重新生成二进制文件

  • 重新安装visual Studio 2013 Pro并重新生成二进制文件

  • 重新安装SSMS(尝试刷新本地内置驱动程序)

  • 从PC卸载所有包含“SQL”的组件,并安装最新的SSM(以减少组件冲突)

  • 仅将可疑组件提取到其自己的控制台应用程序中

  • 尝试使用默认系统DSN而不是明确指定所有参数的SqlDriverConnection()进行SQLConnect()

  • 已将SQL Server ODBC驱动程序13更新为2015.131.4413.46

  • 控制台应用程序源

    // ConsoleTest.cpp : Defines the entry point for the console application.
    //
    #include <windows.h>
    #include <stdio.h>
    #include <tchar.h>
    #include <string>
    #include <sqlext.h>
    #include <sqltypes.h>
    #include <iostream>
    #include "MyTypes.h"
    
    #pragma comment(lib, "ODBC32.lib")
    
    static s32 PrintSqlDiagRecords(SQLRETURN _sqlRet, SQLSMALLINT _sqlHandleType, SQLHANDLE _sqlHandle);
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        std::cout << "Starting test" << std::endl;
    
        SQLHANDLE m_sqlHndlEnvironment = NULL;
        SQLHANDLE m_sqlHndlConnection = NULL;
    
        for (int k = 0; k < 500; ++k)
        {       
            //Allocate environment handle
            SQLRETURN ssiResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_sqlHndlEnvironment);
            if ((ssiResult != SQL_SUCCESS) && (ssiResult != SQL_SUCCESS_WITH_INFO))
            {
                std::cout << "Error allocating ENV handle" << std::endl;
                return -1;
            }
    
            //Set environment attribute
            ssiResult = SQLSetEnvAttr(m_sqlHndlEnvironment, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
            if ((ssiResult != SQL_SUCCESS) && (ssiResult != SQL_SUCCESS_WITH_INFO))
            {
                std::cout << "Error setting ODBC version" << std::endl;
                return -1;
            }
    
            //Allocate connection handle
            ssiResult = SQLAllocHandle(SQL_HANDLE_DBC, m_sqlHndlEnvironment, &m_sqlHndlConnection);
            if ((ssiResult != SQL_SUCCESS) && (ssiResult != SQL_SUCCESS_WITH_INFO))
            {
                std::cout << "Error allocating DBC handle" << std::endl;
                return -1;
            }
    
            (void)SQLSetConnectAttr(m_sqlHndlConnection, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
    
            //Connect SQL
            SQLCHAR acOutmsg[1024] = { 0 };
            ssiResult = SQLDriverConnect(
                m_sqlHndlConnection, 
                NULL,           
                (SQLCHAR*)"My Verified Connection String",
                SQL_NTSL, 
                acOutmsg, 
                sizeof(acOutmsg), 
                NULL, 
                SQL_DRIVER_NOPROMPT);       
            if (ssiResult != SQL_SUCCESS)
            {
                (void) PrintSqlDiagRecords(ssiResult, SQL_HANDLE_DBC, m_sqlHndlConnection);
                //If success with info, just dump diagnostic info
                if (ssiResult == SQL_SUCCESS_WITH_INFO)  {/*Do nothing for now*/ }
                //Else error
                else
                {
                    std::cout << "Error connecting to DB" << std::endl;
                    return -1;
                }
            }       
    
            //Leave out actual DB operation to simplify execution path, but spin here for a while
            Sleep(10);
    
            //Free handles
            SQLRETURN sqlRet = SQL_SUCCESS;
            if (m_sqlHndlConnection != NULL)
            {
                sqlRet = SQLDisconnect(m_sqlHndlConnection);
                if (sqlRet != SQL_SUCCESS)
                {               
                    (void)PrintSqlDiagRecords(ssiResult, SQL_HANDLE_DBC, m_sqlHndlConnection);
                }
    
                sqlRet = SQLFreeHandle(SQL_HANDLE_DBC, m_sqlHndlConnection);
                if (sqlRet != SQL_SUCCESS)
                {
                    std::cout << "Error freeing DBC handle" << std::endl;
                    return -1;
                }
    
                m_sqlHndlConnection = NULL;
            }
    
            //Free environment handle
            if (m_sqlHndlEnvironment != NULL)
            {
                sqlRet = SQLFreeHandle(SQL_HANDLE_ENV, m_sqlHndlEnvironment);
                if (sqlRet != SQL_SUCCESS)
                {
                    std::cout << "Error freeing ENV handle" << std::endl;
                    return -1;
                }
    
                m_sqlHndlEnvironment = NULL;
            }
        }
    
        std::cout << "Test complete" << std::endl;
        Sleep(5000);
        return 0;
    }
    
    s32 PrintSqlDiagRecords(SQLRETURN _sqlRet, SQLSMALLINT _sqlHandleType, SQLHANDLE _sqlHandle)
    {
        SQLRETURN sqlRetDiag = SQL_SUCCESS;
            SQLINTEGER sqliNativeError = SQL_SUCCESS;
            SQLSMALLINT sqlsiMsgLen = 0;
            SQLCHAR acOutmsg[1024] = { 0 };
    
            SQLCHAR acSqlState[1024] = { 0 };
            int i = 1;
            sqlRetDiag = SQLGetDiagRec(_sqlHandleType, _sqlHandle, i, acSqlState, &sqliNativeError, acOutmsg, sizeof(acOutmsg), &sqlsiMsgLen);
            while ((sqlRetDiag != SQL_NO_DATA) && (i < 100))
            {
                //std::cout << "Msg[" << i <<"]: " << acOutmsg << "State: " << acSqlState << std::endl;
                ++i;
                memset(acOutmsg, 0, sizeof(acOutmsg));
                memset(acSqlState, 0, sizeof(acSqlState));
                sqlRetDiag = SQLGetDiagRec(_sqlHandleType, _sqlHandle, i, acSqlState, &sqliNativeError, acOutmsg, sizeof(acOutmsg), &sqlsiMsgLen);
            }
    
            return 0;
    }
    
    //ConsoleTest.cpp:定义控制台应用程序的入口点。
    //
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括“MyTypes.h”
    #pragma注释(lib,“ODBC32.lib”)
    静态s32 PrintSqlDiagRecords(SQLRETURN _sqlRet,SQLSMALLINT _sqlHandleType,SQLHANDLE _SQLHANDLE);
    int _tmain(int argc,_TCHAR*argv[]
    {
    
    std::cout如果您的二进制文件没有什么不同,那么其他一些代码也是。使用
    lm-vsm
    连接windbg并转储所有模块(及其版本)。比较输出。特别注意外来入侵者(注入的DLL)。设置断点并在循环前后使用
    !handle
    查看泄漏的确切内容--不要假设它实际上是任何ODBC对象,它可能是您没有直接分配的辅助对象。感谢@jeroenmoster;非常感谢您的反馈。如果您的操作过程揭示了罪魁祸首,我不确定我是否找到了在这种情况下求助于修复它(除了可能回滚或升级特定的DLL/LIB)。这里的重点是,可能需要大量时间的调查才能找到可行的解决方案,这将需要不确定的时间;考虑到本次调查的开发时间,目前还存在太多的未知因素。我将非常肯定地保留您的建议,以备不时之需。谢谢当做