TVITEM LPARAM to store string - c++

I've got a treeview listing files that are dropped upon it.
When I make a new treeview item, I'd like to store the address of the file as a string in that item, and retrieve it for various nefarious purposes at a later point in time.
Looking at the TVITEM structure in Microsoft docs, apparently LPARAM is the place to store a value:
lParam
Type: LPARAM
A value to associate with the item.
So, I have gone ahead and done that:
TVITEM tvi;
tvi.mask = TVIF_TEXT;
tvi.pszText = const_cast<char *> (str0.c_str());
tvi.cchTextMax = sizeof(tvi.pszText);
tvi.lParam = (LPARAM) foo; // SETTING LPARAM HERE, foo IS A const char *
TVINSERTSTRUCT tvis;
tvis.item = tvi;
tvis.hInsertAfter = 0;
tvis.hParent = hti0;
// Send message to update tree, and return tree item.
return TreeView_InsertItem(tvw_filelist_, &tvis);
Then, when I try to retrieve my value...
HTREEITEM htiSel = TreeView_GetSelection(tvw_filelist_);
TVITEM tvItem;
tvItem.hItem = htiSel;
TreeView_GetItem(tvw_filelist_, &tvItem);
const char * info = (const char *) tvItem.lParam;
MessageBox(NULL, info, "Alert", MB_OK);
...I just get garbage, indicating my pointer went out of scope or is taking a nap or something. The size of that pointer is always 4.
Is this the right way to do what I'm trying to do? If so, what's going on?

Of course, take the time to post a question after a long time trying to figure it out, and the answer shows up in seconds.
Turns out the TVITEM mask needs to include TVIF_PARAM, similar to this question.
If I change the above code to:
tvi.mask = TVIF_TEXT | TVIF_PARAM;
it works as expected.
I'm still not sure if this is the recommended use for LPARAM, though.

struct CustomTreeData
{
LPSTR str; // or even std::string to forget about memory managment
// TODO: any other data you need
};
...
TVITEM tvi;
tvi.mask = TVIF_TEXT | TVIF_PARAM;
CustomTreeData* myDataPtr = new CustomTreeData; // the memory should be free later
myDataPtr->str = stringWhatIWant; // And don't forget to alloc memory for str!
tvi.lParam = (LPARAM) myDataPtr;
I don't check this code, but it should work. Happy coding :)

Related

get lparam value form the htreeitem c++

I created a dialog with a tree control which fetches data on to a list control when clicked on any particular node of the treecontrol. This is how i tried inserting nodes.
CString *sCommonAppkey = new CString(_szApp + sIsPath);
HTREEITEM hrCommon = m_cTreeCtrl.InsertItem(TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM, _T("Common"), icoPlanit, icoPlanit, 0, 0, (LPARAM)(LPCTSTR)sCommonAppkey, NULL, NULL);
When clicked on a node it is being directed to the event handler "OnTvnSelchangedExample"
and the data is fetched from the path specified in "lparam" parameter in the insertitem method of HTREEITEM.
void **CExample**::OnTvnSelchangedExample(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
LPARAM lp = pNMTreeView->itemNew.lParam;
CString *sTempKey = (CString *)lp;
CString path = sTempKey->GetBuffer();
}
I can access the lparam value only in the event handler.
Now i want to implement search functionality for the entire tree's data.
so i need the lparam value of all Tree handles sequentially by iterating through it, so that i can search for the specific text in the tree.
So without clicking on any node of the tree, is there any possibility to get the lparam value of Tree handle(HTREEITEM)
You can iterate through the tree from the root with TreeView_GetChild, there the handle is the tree handle. To get the handle, call TreeView_GetItem.
TVITEMEX item;
item.mask = TVIF_PARAM;
item.hItem = hrCommon;
TreeView_GetItem(handle_, &item);
CString* text = (CString*)item.lParam;
The tree traversal is easy to implement using recursion:
void CMyTreeCtrl::Iterate(HTREEITEM hItem)
{
if (hItem)
{
// Use the tree node corresponding to hItem
// .....
// End of using hItem
hItem = GetNextItem(hItem, TVGN_CHILD);
while (hItem)
{
Iterate(hItem);
hItem = GetNextItem(hItem, TVGN_NEXT);
}
}
else
{
HTREEITEM hItem = GetNextItem(NULL, TVGN_ROOT);
while (hItem)
{
Iterate(hItem);
hItem = GetNextItem(hItem, TVGN_NEXT);
}
}
}
If you want to get the item data you need to simply call GetItemData(hItem). It returns DWORD_PTR. So in your case you need to cast it to CString*. That's it.
IMPORTANT: In this example CMyTreeCtrl is derived from CTreeCtrl.

TabCtrl_GetItem macro not working as expected

I'm creating a basic notepad program, and when the user clicks close, I want it to ask the user if they want to save the current document opened. I'm using a tabbed interface, and trying to retrieve the filename ( text on tab ) so I have a MessageBox that says "Would you like to save: untitled.txt" or similar. I'm having trouble getting the file name. This is what I currently have:
case ID_FILE_CLOSE: // When the close button is clicked
{
HWND hEdit, hTabs;
hTabs = GetDlgItem( hwnd, IDC_MAIN_TAB );
int curTab = TabCtrl_GetCurSel( hTabs );
TCITEM curtitem;
TabCtrl_GetItem( hTabs, curTab, &curtitem );
// Check for file name
MessageBox( hwnd, curtitem.pszText, "Test", MB_OK );
}
break;
This is the error I keep getting in a popup box with Break, Continue, Ignore buttons:
Unhandled exception at 0x7597d298 in notepadpremium.exe: 0xC0000005: Access violation reading location 0xcccccccc.
I'm using MS Visual C++ Express 2010.
I also have a listbox with the filenames that also show the extension ( almost like notepad++ document switcher ) and tried LB_GETITEMDATA through a message, but that always returned blank. I think that was because I use LB_ADDSTRING to add it to the listbox. ( the listbox and tabs are interconnected, when you click on a file in the listbox, it changes to the corresponding tab ). Why isnt my code working the way it should?
Read the documentation:
pitem
Type: LPTCITEM
Pointer to a TCITEM structure that specifies the information to retrieve and receives information about the tab. When the message is sent, the mask member specifies which attributes to return. If the mask member specifies the TCIF_TEXT value, the pszText member must contain the address of the buffer that receives the item text, and the cchTextMax member must specify the size of the buffer.
You are not initializing the TCITEM at all. You need to tell TabCtrl_GetItem() what data to retrieve, and more importantly what buffer you provide to receive that data into. You are not doing any of that, you are passing random data to TabCtrl_GetItem(), which is why it crashes.
Try this instead:
case ID_FILE_CLOSE: // When the close button is clicked
{
HWND hTabs = GetDlgItem( hwnd, IDC_MAIN_TAB );
int curTab = TabCtrl_GetCurSel( hTabs );
TCHAR szFileName[MAX_PATH+1] = {0};
TCITEM curtitem = {0};
curitem.mask = TCIF_TEXT;
curitem.pszText = szFileName;
curitem.cchTextMax = MAX_PATH;
if (TabCtrl_GetItem( hTabs, curTab, &curtitem ))
{
// also from the documentation:
//
// "the control may change the pszText member of the structure
// to point to the new text instead of filling the buffer with
// the requested text. The control may set the pszText member
// to NULL to indicate that no text is associated with the item."
//
// which means you cannot rely on the szFileName[] buffer actually
// containing the filename, you have to use whatever buffer the
// TCITEM is actually pointing at, which may or may not be the
// szFileName buffer...
MessageBox( hwnd, curitem.pszText, TEXT("Test"), MB_OK );
}
}
break;
As for your ListBox issue, you said you are using LB_ADDSTRING to add strings to the ListBox, but are using LB_GETITEMDATA to retrieve them. That is wrong. You need to use LB_GETTEXTLEN and LB_GETTEXT instead. LB_GETITEMDATA is used to retrieve user-defined data that was added to the ListBox using LB_SETITEMDATA.

how to get Selected treeview item text

I'm using WINAPI Treeview to show some data.
I have to get the selected TV_ITEM text, when I select a TV ITEM.
I'm using following code
case WM_NOTIFY:
switch( ((LPNMHDR)lParam)->code)
{
case TVN_SELCHANGED:
{
NMTREEVIEW* pnmtv = (LPNMTREEVIEW)lParam;
LPTSTR str = (pnmtv->itemNew.pszText);
MessageBox( hWnd, str,"load",MB_OK );
}
break;
}
I can't get the correct value, I got some kind of garbage value.
MessageBox shows some kind of special characters(Those are not in keyboard)
Shall anyone help me, how to get the selected item text.
Thanks in advance
Have you read the documentation on TVN_SELCHANGED? It never mentions pszText. You need to
TVITEM item;
item.hItem = pnmtv->itemNew.hItem;
item.mask = TVIF_TEXT;
item.pszText = // allocate buffer
item.cchTextMax = // length of buffer
::SendMessage(hWnd, TVM_GETITEM, 0, (LPARAM)&item);
and then you'll get item.pszText
P.S. If you're working with windows API, how often have you seen Windows allocating and managing strings for you? It just doesn't happen.

Deletion an icon from a tray

Using the code below results in that sometimes an icon remains in a tray right after call to removeIconFromTray method and disappears only after a user moves over an icon in tray.
void CMyDlg::addIconToTray()
{
static HICON hIcon = ::LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON));
NOTIFYICONDATA data;
data.cbSize = sizeof(data);
data.hIcon = hIcon;
data.hWnd = m_hWnd;
strcpy (data.szTip, m_sTrayIconTip.c_str());
data.uFlags = NIF_ICON | NIF_TIP;
data.uID = (UINT)this;
Shell_NotifyIcon (NIM_ADD, &data);
}
void CMyDlg::removeIconFromTray()
{
NOTIFYICONDATA data;
data.cbSize = sizeof(data);
data.hWnd = m_hWnd;
data.uID = (UINT)this;
Shell_NotifyIcon (NIM_DELETE, &data);
}
Whats wrong in this code and how to achieve that an icon disappears from a tray as soon as a code deleting it form there finished working?
One obvious problem is that you are failing to initialize your struct. You should do this:
NOTIFYICONDATA data = { 0 };
Other than that check for errors and call GetLastError to find out what caused any error.
According to MSDN:
Shell_NotifyIcon function
Deletes an icon from the status area. NOTIFYICONDATA structure pointed to by lpdata uses the ID originally assigned to the icon when it was added to the notification area (NIM_ADD) to identify the icon to be deleted.
So, you should pass the same data of NOTIFYICONDATA to Shell_NotifyIcon function.
void CMyDlg::addIconToTray()
{
static HICON hIcon = ::LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON));
NOTIFYICONDATA data;
data.cbSize = sizeof(data);
data.hIcon = hIcon;
data.hWnd = m_hWnd;
strcpy (data.szTip, m_sTrayIconTip.c_str());
data.uFlags = NIF_ICON | NIF_TIP;
data.uID = (UINT)this;
Shell_NotifyIcon (NIM_ADD, &data);
}
void CMyDlg::removeIconFromTray()
{
NOTIFYICONDATA data;
data.cbSize = sizeof(data);
data.hIcon = hIcon;
data.hWnd = m_hWnd;
strcpy (data.szTip, m_sTrayIconTip.c_str());
data.uFlags = NIF_ICON | NIF_TIP;
data.uID = (UINT)this;
Shell_NotifyIcon (NIM_DELETE, &data);
}
This will work properly.
Or, save the data to a member variable.
As DavidHeffernan said, you should zero-initialize your data struct (you should ALWAYS zero-init any struct that you pass to a Win32 API function), eg:
NOTIFYICONDATA data = {0};
Or:
NOTIFYICONDATA data;
ZeroMemory(&data, sizeof(data));
This way, any unused fields have consistent and predictable values. In your case, when calling NIM_DELETE, you are not initializing data, so its uFlags field will have random bits, which is likely to cause Shell_NotifyIcon() to misinterpret the content of your NOTIFYICONDATA and fail, and thus your icon is not removed.

Catching EM_SETCHARFORMAT and modifying the CHARFORMAT data in EN_PROTECTED

I am receiving a EM_CHARFORMAT message in my message handler when SetFont() is called on my custom control. But when I change the data in the CHARFORMAT structure pointed to in the LPARAM, it isn't being used.
void CMyRichEdit::OnProtected(NMHDR* pNMHDR, LRESULT* pResult)
{
ENPROTECTED* pEP = (ENPROTECTED*)pNMHDR;
*pResult = 1;
switch(pEP->msg)
{
case EM_SETCHARFORMAT:
{
CHARFORMAT *cf = reinterpret_cast<CHARFORMAT *>(pEP->lParam);
cf->dwEffects |= (CFE_PROTECTED | CFE_ITALIC);
cf->dwMask |= (CFM_PROTECTED | CFM_ITALIC);
*pResult = 0;
}
break;
The MSDN docs say only if the lParam value changes is it used over the original... but here lParam is a pointer to an object. How can I allocate a new object without the memory leaking?
How can I allocate a new object without the memory leaking?
I'm not sure that reallocating lParam is going to work, but since a window procedure doesn't have to worry about being called on multiple threads, you can point lParam to a static variable safely. That way you don't have to worry about freeing it.
case EM_SETCHARFORMAT:
{
static CHARFORMAT mycf;
CHARFORMAT *cf = reinterpret_cast<CHARFORMAT *>(pEP->lParam);
mycf = *cf;
pEP->lParam = (LPARAM)(LONG_PTR)&mycf;
cf->dwEffects |= (CFE_PROTECTED | CFE_ITALIC);
cf->dwMask |= (CFM_PROTECTED | CFM_ITALIC);
*pResult = 0;
}
break;
However, I really don't think that this is going to solve your problem.