C++ 如何修复C+中的手柄泄漏+;ODBC连接/断开过程?
症状 在对我的C++应用程序进行负载测试时,它使用和ODBC连接不断地(并且大量)与SQL Server实例接口,我开始注意到Windows任务管理器中的句柄泄漏。这些以前没有(请抑制您的怀疑并继续阅读),我怀疑它们是在进行上述负载测试时开发的 在备用计算机上运行完全相同的二进制文件不会显示相同的症状,而是与泄漏开始之前的标准和常见的预期行为相关。也就是说,在不同的机器上使用相同的二进制文件不会发生泄漏 我将此句柄泄漏跟踪到SQL connect/disconnect进程,并能够使用控制台应用程序重新创建,该控制台应用程序仅打开和关闭与SQL Server实例的连接,该连接是以SQL Server实例为模型的。代码如下所示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进程,并能够使用控制台应用程
- 并非所有分配的句柄都已释放李>
- 返回值显示在代码中执行的所有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
// 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)。这里的重点是,可能需要大量时间的调查才能找到可行的解决方案,这将需要不确定的时间;考虑到本次调查的开发时间,目前还存在太多的未知因素。我将非常肯定地保留您的建议,以备不时之需。谢谢当做