ODBC是个老古董,现抛弃之,改为使用 ADO。

在此提醒自己,在要实现任何一个功能之前,应该先充分了解所有实现路径,谨慎选择,以免干了一段时间后才又恍然发现,有更好的方法。


ADO 花了一个早上终于连接并执行数据库操作了,当然花这么多时间,和自己对 vs 的文件夹管理不熟悉有关,在不必要的事情上徒徒浪费时间。

长个记性,头文件、源文件最好还是放在生成的筛选器中,然后加入附加包含目录即可,别搞有的没的。

(另外提醒,在手动 update 数据库时,最好做好备份,千万别把 where 条件弄错了)

参考:

ADO在C++中的使用_阿珊境界-CSDN博客_c++ado

ADO 连接

1.需要包含头文件 icrsint.h,其中有用到的宏

官方例子(实际自己写可有可无):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
_COM_SMARTPTR_TYPEDEF(IADORecordBinding, __uuidof(IADORecordBinding));

class CCustomRs : public CADORecordBinding {
BEGIN_ADO_BINDING(CCustomRs)
ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_ch_fname, sizeof(m_ch_fname), m_ul_fnameStatus, false)
ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_ch_lname, sizeof(m_ch_lname), m_ul_lnameStatus, false)
END_ADO_BINDING()

public:
CHAR m_ch_fname[22];
CHAR m_ch_lname[32];
ULONG m_ul_fnameStatus;
ULONG m_ul_lnameStatus;
};


inline void TESTHR(HRESULT _hr) { if FAILED(_hr) _com_issue_error(_hr); }

2.引入文件 msado15.dll

1
#import "msado15.dll" no_namespace rename("EOF", "EndOfFile")  

该文件在目录 C:\Program Files\Common Files\System\ado 下可以找到,最好把它搬到工程目录下使用,不同系统版本可能不同no_namespace是指忽略命名空间,rename则是把ADO中的EOF重命名为adoEOF。命成什么名字无所谓,但注意声明中的名字要和代码中的名字一致。

3.报错:无法打开源文件 msado15.tlh

  1. 新生成工程
  2. 调整配置管理器,选择 x64 或 x86

4.初始化

初始化:CoInitialize(NULL);

卸载:CoUninitialize();

5.实例化

新建 x.udl,双击配置连接数据库,测试成功后,文本形式打开,文本内容正常为 Provider=SQLOLEDB.1;Persist Security Info=False;User ID=xx;Password=xxxxx;Initial Catalog=DB;Data Source=IP,其中 Password是后加的。

两个方法,推荐第 2 种,方法1 我把握不住。测试时,一开始用方法1还好好的,不知道搞了什么,后面就不能正常查询了。

  1. 方法1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
_RecordsetPtr pRs("ADODB.Recordset");
CCustomRs rs;
IADORecordBindingPtr picRs(pRs);

pRs->Open(L"SELECT * FROM [DCWLW].[dbo].[center]", L"Provider = SQLOLEDB.1; Persist Security Info = False; User ID = xxx; Password = xxxxx; Initial Catalog = DB; Data Source = IP", adOpenStatic, adLockOptimistic, adCmdText);


TESTHR(picRs->BindToRecordset(&rs));

while (!pRs->EndOfFile) {
// Process data in the CCustomRs C++ instance variables.
printf("Name = %s %s\n",
(rs.m_ul_fnameStatus == adFldOK ? rs.m_ch_fname : "<Error>"),
(rs.m_ul_lnameStatus == adFldOK ? rs.m_ch_lname : "<Error>"));

// Move to the next row of the Recordset. Fields in the new row will
// automatically be placed in the CCustomRs C++ instance variables.

pRs->MoveNext();
}
  1. 方法2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
_ConnectionPtr pConn(__uuidof(Connection));

_RecordsetPtr pRec(__uuidof(Recordset));

_CommandPtr pCmd(__uuidof(Command));

pConn.CreateInstance("ADODB.Connection");

pRec.CreateInstance("ADODB.Recordset");

pCmd.CreateInstance("ADODB.Command");

pConn->ConnectionString = "Provider=SQLOLEDB.1;Persist Security Info=False;User ID=xx;Password=xxxxx;Initial Catalog=DB;Data Source=IP";

pConn->Open("", "", "", adConnectUnspecified);

const char* strSQL = "SELECT\
[report_5001_id]\
, [report_str]\
, isnull(row_state, 'good') as row_state\
, [create_date]\
, [barcode]\
, [states]\
, [dy]\
, [dl]\
, [wdmax]\
, [wdmin]\
, [soc]\
, [err_str]\
, [err_str2]\
, [err_str3]\
, [err_str4]\
FROM[DCWLW].[dbo].[report_5001]\
where cast([dl] as decimal) > 50.0 and create_date > '2021'";

pRec = pConn->Execute(_bstr_t(strSQL), NULL, adCmdText);

while (!pRec->EndOfFile)
{

string str = LPSTR(_bstr_t(pRec->GetCollect("barcode")));
cout << str << endl;

//((CListBox*)GetDlgItem(IDC_LIST1))->AddString(str);
pRec->MoveNext();
}

pRec->Close();

pConn->Close();

pRec.Release();

pCmd.Release();

pConn.Release();

6.错误捕获

1
2
3
4
5
6
7
8
9
try{
...
}catch (_com_error& e) {
printf("Error:\n");
printf("Code = %08lx\n", e.Error());
printf("Meaning = %s\n", e.ErrorMessage());
printf("Source = %s\n", (LPCSTR)e.Source());
printf("Description = %s\n", (LPCSTR)e.Description());
}