【问题标题】:Populate treeview with recordset使用记录集填充树视图
【发布时间】:2015-01-11 11:32:47
【问题描述】:

我有一个作为主窗口子窗口的对话框。对话框具有树视图控件。它们是使用资源编辑器和 WinAPI 创建的。

我正在使用 ADO 和 C++ 使用数据库中的一些数据填充树视图。不幸的是,我没有得到预期的结果。

为了理解我的问题,我将提供我从中获取数据的数据库表中的列的描述:

  • ID -> 自动编号
  • BrojUgovora -> 字符串 // 合同编号
  • 许多其他领域...

以下是上表的相关值:

ID | BrojUgovora
-----------------
 1 | qwert
 2 | prvi ugovor
 3 | drugi ugovor

运行代码后,我希望得到以下结果: 药物乌戈沃尔 普维乌戈沃尔 qwert

但我明白了:

drugi ugovor
prvi ugovor
ID           -------> why column name instead of column value???

当我通过 lParam 注释掉我将 ID 存储到树视图节点的部分时,我得到了正确的结果。

我需要将 ID 存储到 lParam 中,因此不能删除我的那部分代码。


这是我在WM_INITDIALOG中调用的函数:

/*********** REMARKS ***************
/**** Fills treeview control with the contract number 
/**** In treeview node's LPARAM is stored the value of the primary key
/**** Returns the number of failed attempts to load string/autonumber field
/***********************************/

int InitTreeView(HWND hDlg)
{
    // error result
    int iNumberOfFailedLoads = 0;

    //connect to database 
    ADODB::_ConnectionPtr pConn("ADODB.Connection");
    try
    {
        HRESULT hr = pConn->Open(bstrConnect, username, password, 
            ADODB::adConnectUnspecified);

        if (!SUCCEEDED(hr))
            throw _com_error(hr);

        ADODB::_CommandPtr pCmd("ADODB.Command");

        pCmd->ActiveConnection = pConn;
        pCmd->CommandType = ADODB::adCmdText;
        pCmd->CommandText = L" select ID, BrojUgovora from UGOVORI;";

        ADODB::_RecordsetPtr pRS = pCmd->Execute(NULL, NULL, ADODB::adCmdText);

        ADODB::Fields* pFields = NULL;

        hr = pRS->get_Fields(&pFields);

        if (!SUCCEEDED(hr))
            throw _com_error(hr);

        if (pFields && pFields->GetCount() > 0)
        {
            while (!pRS->AdoNSEOF)
            {
                // load contract values into treeview
                TVINSERTSTRUCT tvis = { 0 };
                tvis.item.mask = TVIF_TEXT | TVIF_PARAM;
                tvis.hInsertAfter = TVI_FIRST;
                tvis.item.pszText = // this is string field
                    pRS->Fields->GetItem(L"BrojUgovora")->Value.bstrVal;
                tvis.item.lParam =  // this is autonumber field
                    (LPARAM)(pRS->Fields->GetItem(L"ID")->Value.lVal);

                HTREEITEM hti = TreeView_InsertItem(GetDlgItem(hDlg, IDC_TREE1), 
                    &tvis);

                if (NULL == hti)
                    iNumberOfFailedLoads++;

                pRS->MoveNext();
            }
            pRS->Close();
        }

        pConn->Close();
    }
    catch (_com_error &e)
    {
        if (pConn->State == ADODB::adStateOpen)
            pConn->Close();

        iNumberOfFailedLoads = -1;
    }

    return iNumberOfFailedLoads;
}

这里是WM_INITDIALOG 处理程序:

case WM_INITDIALOG:
{
    // needed for visual styles, long story
    EnableThemeDialogTexture(hDlg, ETDT_ENABLETAB);
    InitTreeView(hDlg);
}
    return (INT_PTR)FALSE;

调试结果:

这是我从调试器中得到的:

/*********** REMARKS ***************
/**** Fills treeview control with the contract number 
/**** In treeview node's LPARAM is stored the value of the primary key
/**** Returns the number of failed attempts to load string/autonumber field
/***********************************/

int InitTreeView(HWND hDlg)
{
    // error result
    int iNumberOfFailedLoads = 0;

    //connect to database 
    ADODB::_ConnectionPtr pConn("ADODB.Connection");
    try
    {
        HRESULT hr = pConn->Open(bstrConnect, username, password, 
            ADODB::adConnectUnspecified);

        if (!SUCCEEDED(hr))
            throw _com_error(hr);

        ADODB::_CommandPtr pCmd("ADODB.Command");

        pCmd->ActiveConnection = pConn;
        pCmd->CommandType = ADODB::adCmdText;
        pCmd->CommandText = L" select ID, BrojUgovora from UGOVORI;";

        ADODB::_RecordsetPtr pRS = pCmd->Execute(NULL, NULL, ADODB::adCmdText);

        ADODB::Fields* pFields = NULL;

        hr = pRS->get_Fields(&pFields);

        if (!SUCCEEDED(hr))
            throw _com_error(hr);

        if (pFields && pFields->GetCount() > 0)
        {
            while (!pRS->AdoNSEOF)
            {
                // load contract values into treeview
                TVINSERTSTRUCT tvis = { 0 };
                tvis.item.mask = TVIF_TEXT | TVIF_PARAM;
                tvis.hInsertAfter = TVI_FIRST;
                tvis.item.pszText = 
                    pRS->Fields->GetItem(L"BrojUgovora")->Value.bstrVal;
                tvis.item.lParam =  // here is first breakpoint
                    (LPARAM)(pRS->Fields->GetItem(L"ID")->Value.lVal);

                // MessageBeep(0) added just so I can put breakpoint there
                MessageBeep(0);  // here is second breakpoint

                HTREEITEM hti = TreeView_InsertItem(GetDlgItem(hDlg, IDC_TREE1), 
                    &tvis);

                if (NULL == hti)
                    iNumberOfFailedLoads++;

                pRS->MoveNext();
            }
            pRS->Close();
        }

        pConn->Close();
    }
    catch (_com_error &e)
    {
        if (pConn->State == ADODB::adStateOpen)
            pConn->Close();

        iNumberOfFailedLoads = -1;
    }

    return iNumberOfFailedLoads;
}

当我到达第一个断点时,从数据库中正确读取了字符串。

在第二个断点处,它“神奇地”变为ID

我为解决这个问题所做的努力:

如下更改while 循环中的代码后,一切正常:

while (!pRS->AdoNSEOF)
{
    wchar_t txt[50];
    memset(txt, L'\0', sizeof(txt));
    swprintf_s(txt, 50, L"%s",
        pRS->Fields->GetItem(L"BrojUgovora")->Value.bstrVal);

    //...
    tvis.item.pszText = txt;
    // the rest of the code is the same

我曾尝试使用动态字符串来存储数据库中的字符串,但失败了。我得到调试断言错误。当我尝试使用 wstring 时也会发生同样的情况。

问题:

我应该如何重写我的InitTreeView 函数以避免上面描述的错误?

【问题讨论】:

  • 在迭代条目之前,您似乎错过了对记录集的 MoveFirst 调用。
  • 我很困惑,这是 ADO 问题还是树控制问题?
  • @Jonathan:由于 OP 不知道错误是在添加树视图项目还是在查询数据库条目时出现错误,因此它包含两个标签。一旦问题得到回答,标签/主题就可以适当地更新。
  • @IInspectable 明智地使用调试器应该能让 OP 自己解决这个问题,然后根据需要缩小问题焦点。
  • @JonathanPotter:我尝试过使用调试器,但找不到问题所在。对于我对标签造成的混淆,我深表歉意。我正在努力缩小问题范围。

标签: c++ winapi ado


【解决方案1】:

我怀疑GetItem 方法正在返回一个临时值,一旦函数返回,该值就会丢失;因此pszText 最终指向的字符串已被释放/覆盖。

尝试以下方法,看看是否有效果:

            // load contract values into treeview
            _variant_t varText = pRS->Fields->GetItem(L"BrojUgovora")->Value;
            _variant_t varID = pRS->Fields->GetItem(L"ID")->Value;

            TVINSERTSTRUCT tvis = { 0 };
            tvis.item.mask = TVIF_TEXT | TVIF_PARAM;
            tvis.hInsertAfter = TVI_FIRST;
            tvis.item.pszText = varText.bstrVal;
            tvis.item.lParam =  varID.lVal;

【讨论】:

  • 有效。我也有同样的怀疑,但无法“查明”“罪魁祸首”。看来您是对的-> GetItem 似乎返回了一个临时值,该值在函数返回后丢失。感谢您的帮助-> 投票并正式接受。 根据您之前的评论,我猜这里不需要 ADO 标记。我应该删除还是离开?*
  • @AlwaysLearningNewStuff 很高兴它有效。我会去掉 treeview 标签,因为它与树控件完全无关。
  • 我会去掉 treeview 标签,因为它与树控件完全无关。 完成。如果我将存储ID 的部分省略为TVINSERTSTRUCT lParam 成员,我仍然不明白为什么一切正常。我希望我能得到这个问题的答案......
  • 第二次调用GetItem 覆盖了第一次调用返回的内存。即使不再拥有该内存,它的内容也没有改变,因此它似乎可以工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-23
  • 2014-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多