让【宝码香车】来帮您解答,本回答参考DeepSeek编写,并整理提供。
如果答案能帮助您,请采纳,非常感谢!
.
每日一句正能量,让它带给您好心情:生活中的每一个小进步,都值得被庆祝。
.
针对您在MFC项目中使用ADO时遇到的断线重连问题,我将逐一解答您的疑问。
1.1 如何添加连接配置对话框
您可以创建一个独立的配置对话框类来管理ADO连接参数,这个对话框与CADO类完全解耦。
创建对话框类CConnectConfigDlg:
// ConnectConfigDlg.h
class CConnectConfigDlg : public CDialogEx
{
DECLARE_DYNAMIC(CConnectConfigDlg)
public:
CConnectConfigDlg(CWnd* pParent = nullptr);
virtual ~CConnectConfigDlg();
CString m_strServer;
CString m_strPort;
CString m_strDatabase;
CString m_strUsername;
CString m_strPassword;
protected:
virtual void DoDataExchange(CDataExchange* pDX);
DECLARE_MESSAGE_MAP()
public:
virtual BOOL OnInitDialog();
};
// ConnectConfigDlg.cpp
CConnectConfigDlg::CConnectConfigDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_CONNECT_CONFIG_DIALOG, pParent)
, m_strServer(_T(""))
, m_strPort(_T("1433"))
, m_strDatabase(_T(""))
, m_strUsername(_T(""))
, m_strPassword(_T(""))
{
}
void CConnectConfigDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT_SERVER, m_strServer);
DDX_Text(pDX, IDC_EDIT_PORT, m_strPort);
DDX_Text(pDX, IDC_EDIT_DATABASE, m_strDatabase);
DDX_Text(pDX, IDC_EDIT_USERNAME, m_strUsername);
DDX_Text(pDX, IDC_EDIT_PASSWORD, m_strPassword);
}
BOOL CConnectConfigDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 可以从注册表或配置文件中加载上次的连接设置
return TRUE;
}
在CADO类中添加重新连接的方法:
// CADO.h
class CADO
{
public:
BOOL Reconnect(LPCTSTR server, LPCTSTR port, LPCTSTR database,
LPCTSTR username, LPCTSTR password);
BOOL IsConnectionValid();
private:
_ConnectionPtr m_pConnection;
_com_error m_error;
};
// CADO.cpp
BOOL CADO::Reconnect(LPCTSTR server, LPCTSTR port, LPCTSTR database,
LPCTSTR username, LPCTSTR password)
{
try {
if (m_pConnection != NULL && m_pConnection->State == adStateOpen) {
m_pConnection->Close();
}
CString strConnection;
strConnection.Format(_T("Provider=SQLOLEDB.1;Data Source=%s,%s;Initial Catalog=%s;User ID=%s;Password=%s"),
server, port, database, username, password);
m_pConnection.CreateInstance(__uuidof(Connection));
m_pConnection->Open((_bstr_t)strConnection, "", "", adConnectUnspecified);
return TRUE;
}
catch (_com_error &e) {
m_error = e;
return FALSE;
}
}
BOOL CADO::IsConnectionValid()
{
try {
if (m_pConnection == NULL || m_pConnection->State != adStateOpen) {
return FALSE;
}
// 执行一个简单的测试查询
_RecordsetPtr pRst = NULL;
pRst = m_pConnection->Execute("SELECT 1", NULL, adCmdText);
return TRUE;
}
catch (_com_error &e) {
m_error = e;
return FALSE;
}
}
1.2 关于错误捕获的问题
您可以在调用封装函数时进行二次错误捕获,这是完全可行的。ADO错误通常通过_com_error异常抛出,可以在不同层级进行捕获。
修改后的调用代码:
void CDemoDlg::OnBnClickedButton1()
{
int nRetryCount = 0;
const int MAX_RETRY = 2;
while (nRetryCount <= MAX_RETRY) {
try {
g_app_ado.RstAddNew();
break; // 成功执行,跳出循环
}
catch (_com_error &e) { // 注意:这里应该是引用,不是指针
nRetryCount++;
// 检查是否是网络连接错误
if (IsNetworkError(e) && nRetryCount <= MAX_RETRY) {
if (HandleConnectionError() == IDOK) {
// 用户成功重新连接,继续重试
continue;
} else {
// 用户取消重连
break;
}
} else {
// 其他类型的错误或重试次数已用完
CString strError;
strError.Format(_T("操作失败,错误:%s"), (LPCTSTR)e.ErrorMessage());
AfxMessageBox(strError);
break;
}
}
}
}
BOOL CDemoDlg::IsNetworkError(_com_error &e)
{
HRESULT hr = e.Error();
// 这些是常见的连接相关错误代码
return (hr == 0x80004005 || // 未指定的错误
hr == 0x80040E14 || // 无法连接到服务器
hr == 0x80040E19 || // 无法打开连接
hr == 0x80040E21 || // 多步操作产生错误
hr == 0x80040E35); // 事务中的连接无法被枚举
}
int CDemoDlg::HandleConnectionError()
{
CConnectConfigDlg dlg;
// 可以在这里设置默认值或从配置加载
if (dlg.DoModal() == IDOK) {
if (g_app_ado.Reconnect(dlg.m_strServer, dlg.m_strPort,
dlg.m_strDatabase, dlg.m_strUsername,
dlg.m_strPassword)) {
AfxMessageBox(_T("重新连接成功!"));
return IDOK;
} else {
AfxMessageBox(_T("重新连接失败,请检查连接参数!"));
return IDCANCEL;
}
}
return IDCANCEL;
}
1.3 重新执行出错的操作
对于需要重新执行的操作,建议使用重试机制。您需要在捕获到连接错误后,在用户成功重新连接后重新调用原来的方法。
改进的重试机制实现:
// 通用的数据库操作执行函数
template<typename Func, typename... Args>
BOOL ExecuteWithRetry(Func&& func, Args&&... args)
{
int nRetryCount = 0;
const int MAX_RETRY = 3;
while (nRetryCount <= MAX_RETRY) {
try {
// 执行传入的函数
std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
return TRUE;
}
catch (_com_error &e) {
nRetryCount++;
if (IsNetworkError(e) && nRetryCount <= MAX_RETRY) {
CConnectConfigDlg dlg;
if (dlg.DoModal() == IDOK) {
if (g_app_ado.Reconnect(dlg.m_strServer, dlg.m_strPort,
dlg.m_strDatabase, dlg.m_strUsername,
dlg.m_strPassword)) {
// 重新连接成功,继续下一次重试
continue;
}
}
// 重连失败或用户取消,退出重试
break;
} else {
// 非网络错误或重试次数用完,直接抛出异常
throw;
}
}
}
return FALSE;
}
// 使用示例
void CDemoDlg::OnBnClickedButton1()
{
auto operation = []() {
g_app_ado.RstAddNew();
// 可以在这里添加其他相关操作
g_app_ado.RstUpdate();
};
if (!ExecuteWithRetry(operation)) {
AfxMessageBox(_T("操作失败,请检查网络连接!"));
} else {
AfxMessageBox(_T("操作成功!"));
}
}
更简单的实现方式(如果不想使用模板):
BOOL CDemoDlg::ExecuteDatabaseOperation()
{
int nRetryCount = 0;
const int MAX_RETRY = 3;
while (nRetryCount <= MAX_RETRY) {
try {
// 这里是需要执行的数据库操作序列
g_app_ado.RstAddNew();
// 其他数据库操作...
g_app_ado.RstUpdate();
return TRUE; // 成功执行
}
catch (_com_error &e) {
nRetryCount++;
if (IsNetworkError(e) && nRetryCount <= MAX_RETRY) {
if (!ShowReconnectDialog()) {
return FALSE; // 用户取消重连
}
// 重连成功,继续下一次循环(重试操作)
} else {
// 其他错误,显示错误信息
DisplayErrorMessage(e);
return FALSE;
}
}
}
return FALSE;
}
总结建议
- 连接检测:在执行重要操作前,可以先调用
IsConnectionValid()检测连接状态 - 超时设置:在连接字符串中设置连接超时和命令超时
- 配置持久化:将成功的连接配置保存到注册表或配置文件中
- 自动重连:对于短暂网络中断,可以实现自动重连而不显示对话框
- 状态提示:在界面显示当前连接状态,让用户了解连接情况
这种设计确保了CADO类与重连逻辑的解耦,同时提供了灵活的错误处理和重试机制。