Reputation: 3031
I have a dialog box that behaves as a child of the main window. Dialog box has treeview control. They are created using resource editor and WinAPI.
I am using ADO and C++ to populate treeview with some data from database. Unfortunately, I do not get the expected result.
To understand my problem, I will provide the description of the columns in the database table I get data from:
Here are the relevant values for the above table:
ID | BrojUgovora
-----------------
1 | qwert
2 | prvi ugovor
3 | drugi ugovor
After I run my code, I expect to get following result: drugi ugovor prvi ugovor qwert
but I get this:
drugi ugovor
prvi ugovor
ID -------> why column name instead of column value???
When I comment out the part where I store ID into treeview node via lParam, I get proper result.
I need to store the ID into lParam, so deleting that part of my code is not an option.
Here is the function I call in 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;
}
Here is WM_INITDIALOG
handler:
case WM_INITDIALOG:
{
// needed for visual styles, long story
EnableThemeDialogTexture(hDlg, ETDT_ENABLETAB);
InitTreeView(hDlg);
}
return (INT_PTR)FALSE;
Here is what I get from the debugger:
/*********** 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;
}
When I reach first breakpoint, string is correctly read from the database.
On second breakpoint, it "magically" changes to ID
.
After changing the code in the while
loop like below, everything works:
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
I have tried to use dynamic string to store the string from database, but have failed. I get debug assertion error. The same happens when I try to use wstring
.
How should I rewrite my InitTreeView
function to avoid bug I described above?
Upvotes: 0
Views: 325
Reputation: 37192
I suspect that the GetItem
method is returning a temporary value which is being lost once the function returns; so the string that pszText
ends up pointing to has been freed/overwritten.
Try the following and see if it makes a difference:
// 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;
Upvotes: 1